{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "github地址:[https://github.com/cheesezh/python_design_patterns](https://github.com/cheesezh/python_design_patterns)\n",
    "\n",
    "## 组合模式\n",
    "\n",
    "组合模式，将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和对组合对象的使用具有一致性[DP]。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "- root\n",
      "--- Leaf A\n",
      "--- Leaf B\n",
      "--- Composite X\n",
      "----- Leaf XA\n",
      "----- Leaf XB\n",
      "----- Composite XY\n",
      "------- Leaf XYA\n",
      "------- Leaf XYB\n",
      "--- Leaf C\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Component():\n",
    "    \"\"\"\n",
    "    Component为组合中的对象声明接口，在适当情况下，实现所有类共有接口的默认行为。\n",
    "    声明一个接口用于访问和管理Component的子部件。\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    @abstractmethod\n",
    "    def add(self, c):\n",
    "        \"\"\"\n",
    "        通常用add来增加树枝或树叶\n",
    "        \"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def remove(self, c):\n",
    "        \"\"\"\n",
    "        通常用remove来删除树枝或树叶\n",
    "        \"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def display(self, depth):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class Leaf(Component):\n",
    "    \"\"\"\n",
    "    叶子节点\n",
    "    \"\"\"\n",
    "    def add(self, c):\n",
    "        print(\"叶子节点无法添加子节点\")\n",
    "        \n",
    "    def remove(self, c):\n",
    "        print(\"叶子节点无法删除子节点\")\n",
    "        \n",
    "    def display(self, depth):\n",
    "        print(\"-\"*depth, self.name)\n",
    "        \n",
    "\n",
    "class Composite(Component):\n",
    "    \"\"\"\n",
    "    子部件节点\n",
    "    \"\"\"\n",
    "    def __init__(self, name):\n",
    "        super().__init__(name)\n",
    "        self.children = []\n",
    "        \n",
    "    def add(self, c):\n",
    "        self.children.append(c)\n",
    "        \n",
    "    def remove(self, c):\n",
    "        self.children.remove(c)\n",
    "        \n",
    "    def display(self, depth):\n",
    "        print(\"-\"*depth, self.name)\n",
    "        for c in self.children:\n",
    "            c.display(depth+2)\n",
    "            \n",
    "            \n",
    "def main():\n",
    "    root = Composite(\"root\")\n",
    "    root.add(Leaf(\"Leaf A\"))\n",
    "    root.add(Leaf(\"Leaf B\"))\n",
    "    \n",
    "    comp = Composite(\"Composite X\")\n",
    "    comp.add(Leaf(\"Leaf XA\"))\n",
    "    comp.add(Leaf(\"Leaf XB\"))\n",
    "    \n",
    "    root.add(comp)\n",
    "    \n",
    "    comp2 = Composite(\"Composite XY\")\n",
    "    comp2.add(Leaf(\"Leaf XYA\"))\n",
    "    comp2.add(Leaf(\"Leaf XYB\"))\n",
    "    \n",
    "    comp.add(comp2)\n",
    "    \n",
    "    root.add(Leaf(\"Leaf C\"))\n",
    "    \n",
    "    leaf_d = Leaf(\"Leaf D\")\n",
    "    root.add(leaf_d)\n",
    "    root.remove(leaf_d)\n",
    "    \n",
    "    root.display(1)\n",
    "    \n",
    "main()\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 透明方式与安全方式\n",
    "\n",
    "Leaf类中也有Add和Reomve，但是树叶不可以再长分枝。这种方式叫做透明方式，也就是说再Component中声明所有用来管理子对象的方法，其中包括add，remove等。这样Component抽象类的所有子类都具备了add和remove。这样的好处在于叶子节点和分枝节点对于外界没有区别，它们具备完全一致的行为接口。但是问题也比较明显，因为Leaf类本身不具备add和remove等功能，所以实现它是没有意义的。\n",
    "\n",
    "另一种是安全方式，也就是在Component接口中不去声明add和remove方法，那么子类Leaf也就不需要去实现它，而是在Composite声明所有用来管理子类对象的方法，这样做就不会出现刚才提到的问题，不过由于不透明，所以树叶和树枝类将有不同的接口，客户端调用需要做相应的判断，带来了不便。\n",
    "\n",
    "### 何时使用组合模式\n",
    "\n",
    "当需求中是体现部分与整体层次的结构时，以及希望用户可以忽略组合对象与单个对象的不同，统一地使用组合结构中的所有对象时，就应该考虑用组合模式了。\n",
    "\n",
    "## 题目\n",
    "使用组合模式，模拟公司管理系统。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "组织架构图\n",
      "- 北京总公司\n",
      "--- 总公司人力资源部\n",
      "--- 总公司财务部\n",
      "--- 上海华东分公司\n",
      "----- 华东分公司人力资源部\n",
      "----- 华东分公司财务部\n",
      "----- 南京办事处\n",
      "------- 南京办事处人力资源部\n",
      "------- 南京办事处财务部\n",
      "----- 杭州办事处\n",
      "------- 杭州办事处人力资源部\n",
      "------- 杭州办事处财务部\n",
      "履行职责\n",
      "总公司人力资源部负责员工招聘。\n",
      "总公司财务部负责财务收支。\n",
      "华东分公司人力资源部负责员工招聘。\n",
      "华东分公司财务部负责财务收支。\n",
      "南京办事处人力资源部负责员工招聘。\n",
      "南京办事处财务部负责财务收支。\n",
      "杭州办事处人力资源部负责员工招聘。\n",
      "杭州办事处财务部负责财务收支。\n"
     ]
    }
   ],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Company():\n",
    "    \"\"\"\n",
    "    抽象公司类\n",
    "    \"\"\"\n",
    "    __metaclass__ = ABCMeta\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        \n",
    "    @abstractmethod\n",
    "    def add(self, c):\n",
    "        \"\"\"\n",
    "        通常用add来增加树枝或树叶\n",
    "        \"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def remove(self, c):\n",
    "        \"\"\"\n",
    "        通常用remove来删除树枝或树叶\n",
    "        \"\"\"\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def display(self, depth):\n",
    "        pass\n",
    "    \n",
    "    @abstractmethod\n",
    "    def line_of_duty(self):\n",
    "        pass\n",
    "    \n",
    "\n",
    "class ConcreteCompany(Company):\n",
    "    \"\"\"\n",
    "    具体公司类\n",
    "    \"\"\"\n",
    "    def __init__(self, name):\n",
    "        super().__init__(name)\n",
    "        self.children = []\n",
    "        \n",
    "    def add(self, c):\n",
    "        self.children.append(c)\n",
    "        \n",
    "    def remove(self, c):\n",
    "        self.children.remove(c)\n",
    "        \n",
    "    def display(self, depth):\n",
    "        print(\"-\"*depth, self.name)\n",
    "        for c in self.children:\n",
    "            c.display(depth+2)\n",
    "            \n",
    "    def line_of_duty(self):\n",
    "        for c in self.children:\n",
    "            c.line_of_duty()\n",
    "            \n",
    "\n",
    "class HRDepartment(Company):\n",
    "    \"\"\"\n",
    "    人力资源部\n",
    "    \"\"\" \n",
    "    def add(self, c):\n",
    "        pass\n",
    "        \n",
    "    def remove(self, c):\n",
    "        pass\n",
    "        \n",
    "    def display(self, depth):\n",
    "        print(\"-\"*depth, self.name)\n",
    "            \n",
    "    def line_of_duty(self):\n",
    "        print(\"{}负责员工招聘。\".format(self.name))\n",
    "        \n",
    "        \n",
    "class FinanceDepartment(Company):\n",
    "    \"\"\"\n",
    "    财务部\n",
    "    \"\"\" \n",
    "    def add(self, c):\n",
    "        pass\n",
    "        \n",
    "    def remove(self, c):\n",
    "        pass\n",
    "        \n",
    "    def display(self, depth):\n",
    "        print(\"-\"*depth, self.name)\n",
    "            \n",
    "    def line_of_duty(self):\n",
    "        print(\"{}负责财务收支。\".format(self.name))\n",
    "            \n",
    "            \n",
    "def main():\n",
    "    root = ConcreteCompany(\"北京总公司\")\n",
    "    root.add(HRDepartment(\"总公司人力资源部\"))\n",
    "    root.add(FinanceDepartment(\"总公司财务部\"))\n",
    "    \n",
    "    comp = ConcreteCompany(\"上海华东分公司\")\n",
    "    comp.add(HRDepartment(\"华东分公司人力资源部\"))\n",
    "    comp.add(FinanceDepartment(\"华东分公司财务部\"))\n",
    "    root.add(comp)\n",
    "    \n",
    "    comp1 = ConcreteCompany(\"南京办事处\")\n",
    "    comp1.add(HRDepartment(\"南京办事处人力资源部\"))\n",
    "    comp1.add(FinanceDepartment(\"南京办事处财务部\"))\n",
    "    comp.add(comp1)\n",
    "    \n",
    "    comp2 = ConcreteCompany(\"杭州办事处\")\n",
    "    comp2.add(HRDepartment(\"杭州办事处人力资源部\"))\n",
    "    comp2.add(FinanceDepartment(\"杭州办事处财务部\"))\n",
    "    comp.add(comp2)\n",
    "    \n",
    "    print(\"组织架构图\")\n",
    "    root.display(1)\n",
    "    \n",
    "    print(\"履行职责\")\n",
    "    root.line_of_duty()\n",
    "    \n",
    "main()\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "组合模式定义了包含人力资源部和财务部这些基本对象和分公司，办事处等组合对象的类层次结构。基本对象可以被组合成共复杂的组合对象，而这个组合对象又可以被组合，这样不断地递归下去，客户端代码中，任何用到基本对象的地方都可以使用组合对象了。用户不用关心到底是处理一个叶子节点还是处理一个组合组件，也用不着为定义组合而写一些选择判断语句。\n",
    "\n",
    "简单的说，组合模式让客户可以一致地使用组合结构和单个对象。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
